#include "LecteurPhraseAvecArbre.h"
#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts() {
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse() {
	arbre=programme();
	cout << "Syntaxe correcte." << endl;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme() {
	// <programme> ::= debut <seq_inst> fin FIN_FICHIER

	sauterSymCour("debut");
	Noeud* si = seqInst();
	sauterSymCour("fin");
	testerSymCour("<FINDEFICHIER>");
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst() {
	// <seqInst> ::= <inst> ; { <inst> ; }

	NoeudSeqInst* si = new NoeudSeqInst();
	do {
		si->ajouteInstruction(inst());
		sauterSymCour(";");
	} while (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour() == "si" || ls.getSymCour() == "tantque" || ls.getSymCour() == "repeter" || ls.getSymCour() == "lire" || ls.getSymCour()=="pour" || ls.getSymCour()=="ecrire"|| ls.getSymCour()=="choisir");
	// tant que le symbole courant est un debut possible d'instruction...
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst() {
// <inst> ::= <affectation> | <inst_condi>
        if (ls.getSymCour()=="<VARIABLE>")
                return affectation();
        else if (ls.getSymCour()=="si") // on lance la "conditionr qui contiendra elle-meme des instructions. (condition = ExpBool)
                return instSi();
        else if (ls.getSymCour()=="tantque") // on lance la "conditionr qui contiendra elle-meme des instructions. (condition = ExpBool)
                return instTq();
        else if (ls.getSymCour()=="pour") // on lance la "conditionr qui contiendra elle-meme des instructions. (condition = ExpBool)
                return instPour();
        else if (ls.getSymCour()=="repeter") // on lance la "conditionr qui contiendra elle-meme des instructions. (condition = ExpBool)
                return instRepeter();
        else if (ls.getSymCour()=="lire") // on lance la "conditionr qui contiendra elle-meme des instructions. (condition = ExpBool)
                return instLire();
        else if (ls.getSymCour()=="ecrire") // on lance la "conditionr qui contiendra elle-meme des instructions. (condition = ExpBool)
                return instEcrire();
        else{
        erreur("<inst>");
        return NULL;
        }
	
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> = <expression>

	testerSymCour("<VARIABLE>");
        ls.suivant();
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	sauterSymCour("=");
	Noeud* exp = expression();
	return new NoeudAffectation(var,exp);
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <terme> { <opAdd> <terme> }

	Noeud* term = terme();
        while (ls.getSymCour()=="+" || ls.getSymCour()=="-") {
                Symbole operateurAdd = opAdd(); // on stocke le symbole de l'opérateur
                Noeud* termDroit=terme(); // lecture de l'operande droit
		term = new NoeudOperateurBinaire(operateurAdd,term,termDroit); // const. du noeud
	}
	return term;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur() {
//<facteur> ::= <entier> | <chaine> | <variable> | <opUnaire> <expEt> | ( <expEt> )


	Noeud* fact=NULL;

	if (ls.getSymCour()=="<VARIABLE>" || ls.getSymCour()=="<ENTIER>"|| ls.getSymCour()=="<CHAINE>") {
		fact=ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	} else if (ls.getSymCour()=="-"|| ls.getSymCour()=="non") {
		ls.suivant();
		// on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
                Symbole operateur = opUnaire();
		fact = new NoeudOperateurUnaire(operateur, expEt());
	} else if (ls.getSymCour()=="(") {
		ls.suivant();
		fact=expEt();
		sauterSymCour(")");
	} else
		erreur("<facteur>");
	return fact;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expOu() 
// <expEt> ::= <expOu> { <opEt> <expOu> }
{ 
        Noeud* rel = relation();
        while (ls.getSymCour()=="ou") {
                Symbole operateur = opOu();
                Noeud* nRelDroit = relation();
                rel = new NoeudOperateurBinaire(operateur, rel, nRelDroit);
        }
        return rel;
}
////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::expEt() {
// <expOu> ::= <relation> { <opOu> <relation> }

        Noeud* ExpOu = expOu();
        while (ls.getSymCour()=="et") {
                Symbole operateur = opEt();
                Noeud* ExpOuDroit = expOu();
                ExpOu = new NoeudOperateurBinaire(operateur, ExpOu, ExpOuDroit);
        }
        return ExpOu;
}

////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::relation() 
 // <relation> ::= <expression> { <opRel> <expression> }
{
        Noeud* exp = expression();
        while(ls.getSymCour()=="==" || ls.getSymCour()=="!=" || ls.getSymCour()=="<" || ls.getSymCour()=="<=" || ls.getSymCour()==">" || ls.getSymCour()==">=") {
                Symbole operateur = opRel();
                Noeud* nExpDroit = expression();
                exp = new NoeudOperateurBinaire(operateur, exp, nExpDroit);
        }
        return exp;
}


////////////////////////////////////////////////////////////////////////////////

Noeud* LecteurPhraseAvecArbre::terme()
// <terme> ::= <facteur> { <opMult> facteur> }
{
    Noeud* r = facteur();
    while (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        r = new NoeudOperateurBinaire(opMult(), r, facteur());
    }
    return r;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opAdd()
// <opAdd> ::=  + | -
{
    Symbole op;
    if (ls.getSymCour() == "+" || ls.getSymCour() == "-") {
        op = ls.getSymCour();
        ls.suivant();
        
    } else{
        erreur("<opAdd>");
    }
    return op;
}

////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opMult()
// <opMult> ::=  * | /
{
    Symbole op;
    if (ls.getSymCour() == "*" || ls.getSymCour() == "/") {
        op = ls.getSymCour();
        ls.suivant();
        
    } else{
        erreur("<opMult>");
    }
    return op;
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opUnaire() {
    // <opUnaire> ::= - | non
    Symbole op;
    if (ls.getSymCour() == "-" || ls.getSymCour() == "non") {
        op = ls.getSymCour();
        ls.suivant();
        
    } else{
        erreur("<opUnaire>");
    }
    return op;
    
}
////////////////////////////////////////////////////////////////////////////////

Symbole LecteurPhraseAvecArbre::opRel() {
    // <opRel> ::= == | != | < | <= | > | >=
    Symbole op;
    if (ls.getSymCour() == "==" || ls.getSymCour() == "!=" || ls.getSymCour() == ">=" || ls.getSymCour() == "<=" || ls.getSymCour() == "<" || ls.getSymCour() == ">") {
        op = ls.getSymCour();
        ls.suivant();
        
    } else{
        erreur("<opRel>");
    }
    return op;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opEt() {
// <opEt> ::= et
        Symbole operateur;
        if (ls.getSymCour()=="et") {
                operateur=ls.getSymCour();
                ls.suivant();
        }
        else
                erreur("<opEt>");
        return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Symbole LecteurPhraseAvecArbre::opOu() {
// <opOu> ::= ou
        Symbole operateur;
        if (ls.getSymCour()=="ou") {
                operateur=ls.getSymCour();
                ls.suivant();
        }
        else
                erreur("<opOu>");
        return operateur;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instSi(){
// <instSi> ::= si ( <expEt> ) <seqInst> { sinonsi ( <expEt> ) <seqInst> } [ sinon <seqInst> ] finsi

        Noeud* exp=NULL;
        Noeud* suivant=NULL;
        Noeud* si=NULL;
        Noeud* seqinst=NULL;
         
        if (ls.getSymCour()=="si") {

                sauterSymCour("si");
                sauterSymCour("(");
                exp = expEt();
                sauterSymCour(")");
                seqinst = seqInst();
                suivant = instSi();
                sauterSymCour("finsi");
                si = new NoeudSi(exp,seqinst, suivant);
        }
        else if (ls.getSymCour()=="sinonsi") {

                sauterSymCour("sinonsi");
                sauterSymCour("(");
                exp = expEt();
                sauterSymCour(")");
                seqinst = seqInst();
                suivant = instSi();
                si = new NoeudSi(exp,seqinst, suivant);
                
        }
        else if (ls.getSymCour()=="sinon") {
               
                sauterSymCour("sinon");
                seqinst = seqInst();
                exp = ts.chercheAjoute(Symbole("1"));
                si = new NoeudSi(exp,seqinst,suivant);
               
                
        } 
        
        return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instTq()
// <instTq> ::= tantque ( <expOu> ) <seqInst> fintanque
{
        sauterSymCour("tantque");
        sauterSymCour("(");
        Noeud* expB = expOu();
        sauterSymCour(")");
        Noeud* seq = seqInst();
        NoeudInstTq* tq = new NoeudInstTq(expB, seq);
        sauterSymCour("fintantque");
        return tq;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instPour()
// <instPour> ::= pour (<affectation>;<expOu>;<affectation>) <seqInst> finpour
{
        sauterSymCour("pour");
        sauterSymCour("(");
        Noeud* aff1 = affectation();
        sauterSymCour(";");
        Noeud* expB = expOu();
        sauterSymCour(";");
        Noeud* aff2 = affectation();
        sauterSymCour(")");
        Noeud* seq = seqInst();
        NoeudInstPour* pou = new NoeudInstPour(aff1, expB, aff2, seq);
        sauterSymCour("finpour");
        return pou;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instRepeter()
// <instRepeter> ::= repeter <seqInst> jusqua ( <expOu> )
{
        sauterSymCour("repeter");
        Noeud* seq = seqInst();
        sauterSymCour("jusqua");
        sauterSymCour("(");
        Noeud* exp = expOu();
        sauterSymCour(")");
        NoeudInstRep* rep = new NoeudInstRep(exp, seq);
        return rep;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instLire()
//<instLire> ::= lire ( <variable> )
{
        NoeudInstLire* li = NULL;
        sauterSymCour("lire");
        sauterSymCour("(");
        if (ls.getSymCour()=="<VARIABLE>"){
                ts.chercheAjoute(ls.getSymCour());
                Noeud* var = ts.chercheAjoute(ls.getSymCour());
                ls.suivant();
                li = new NoeudInstLire(var);
                sauterSymCour(")");
        }

        return li;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::instEcrire()
//<instEcrire> ::= ecrire ( <expression> | <chaine> )
{
        NoeudInstEcrire* ec = NULL;
        sauterSymCour("ecrire");
        sauterSymCour("(");
        if (ls.getSymCour() == "<CHAINE>"){
                Noeud* ch = ts.chercheAjoute(ls.getSymCour());
                ls.suivant();
                ec = new NoeudInstEcrire(NULL, ch);
                sauterSymCour(")");
        }
        else{
                Noeud* exp = expression();
                ls.suivant();
                ec = new NoeudInstEcrire(exp, NULL);
        }
        return ec;
}


////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		cout << endl << "-------- Erreur ligne " << ls.getLigne()
				<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
				<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
				<< endl;
		exit(0); // plus tard, on levera une exception
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur(string mess) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << mess << endl
			<< "   Trouve  : " << ls.getSymCour() << endl << endl;
	exit(0); // plus tard, on levera une exception
}
